﻿using System;
using System.Collections;
using System.Data;
using System.Linq;
using System.Text.RegularExpressions;
using DGen;
using System.Diagnostics;
using NLog;

namespace Tab2Sql
{
    /// <summary>
    /// you
    /// </summary>
    public class Document
    {

        private static Logger logger = LogManager.GetCurrentClassLogger ();
        internal System.Diagnostics.Stopwatch stopClock;

        /// <summary>
        ///
        /// </summary>
        public int GlobalCounter
        {
            get {
                if (Words != null)
                {
                    return Words.globalCounter;
                } else
                {
                    return 0;
                }
            }
        }
        /// <summary>
        ///
        /// </summary>
        public bool useSpecialBoundaryRules;

        /// <summary>
        /// Because the types of the fields have indexers, these fields appear as "indexed properties":
        /// </summary>
        public readonly CharacterCollection Characters;

        /// <summary>
        ///  Because the types of the fields have indexers, these fields appear as "indexed properties":
        /// </summary>
        public readonly WordCollection Words;

        private char[] TextArray; // The text of the document. 

        /// <summary>
        /// Constructor - supply text to be analysed.
        /// </summary>
        /// <param name="initialText"></param>
        public Document(string initialText)
        {
            stopClock = new System.Diagnostics.Stopwatch();
            stopClock.Start();
            TextArray = initialText.ToCharArray();
            Words = new WordCollection(this);
            Characters = new CharacterCollection(this);
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="myArrayList">
        /// A <see cref="ArrayList"/>
        /// </param>
        /// <param name="useSpecialBoundariesX">
        /// A <see cref="System.Boolean"/>
        /// </param>
        public Document(ArrayList myArrayList, bool useSpecialBoundariesX)
        {
            stopClock = new System.Diagnostics.Stopwatch();
            stopClock.Start();
            useSpecialBoundaryRules = useSpecialBoundariesX;
            System.Text.StringBuilder sb = new System.Text.StringBuilder(200000);
            foreach (var x in myArrayList.ToArray(typeof(string)))
            {
                sb.Append(x.ToString().Trim());
            }
            string initialText = sb.ToString();
            
            TextArray = initialText.ToCharArray();
            Words = new WordCollection(this);
            Characters = new CharacterCollection(this);
            Trace.TraceInformation("Document constructor complete. {0} ms.", stopClock.ElapsedMilliseconds);
        }




        //public Document(string initialText, bool useSpecialBoundariesX)
        //{
        //    //stopClock = new System.Diagnostics.Stopwatch();
        //    //stopClock.Start();
        //    //useSpecialBoundaryRules = useSpecialBoundariesX;
        //    //TextArray = initialText.ToCharArray();
        //    //Words = new WordCollection(this, useSpecialBoundaryRules);
        //    //Trace.TraceInformation(stopClock.ElapsedTicks.ToString() + "      word collection done. now char collection");
        //    //Characters = new CharacterCollection(this);
           
        //}
        /// <summary>
        /// returns starting text
        /// </summary>
        public string Text
        {
            get { return new string(TextArray); }
        }

        #region Nested type: CharacterCollection

        /// <summary>
        /// Type allowing the document to be viewed like an "array" of characters:
        /// </summary>
        public class CharacterCollection
        {
            private static Logger logger = LogManager.GetCurrentClassLogger ();
            private readonly Document document; // The containing document

            internal CharacterCollection(Document d)
            {
                document = d;
            }

            // Indexer to get and set characters in the containing document:
            /// <summary>
            ///
            /// </summary>
            /// <param name="index">
            /// A <see cref="System.Int32"/>
            /// </param>
            public char this[int index]
            {
                get { return document.TextArray[index]; }
                set { document.TextArray[index] = value; }
            }


            /// <summary>
            /// Get the count of characters in the containing document: 
            /// </summary>
            public int Count
            {
                get { return document.TextArray.Length; }
            }
        }

        #endregion

        #region Nested type: WordCollection

        /// <summary>
        /// Type allowing the document to be viewed like an array of words:
        /// </summary>
        public class WordCollection
        {
            private static Logger logger = LogManager.GetCurrentClassLogger ();
            /// <summary>
            ///
            /// </summary>
            public int globalCounter = 0;


            private readonly Document document; // The containing document
            private bool useSpecialWordBoundaries;
            private char[] GetSpecialBoundaries
            {
                get
                {
                    return theSpecialBoundaries;
                }
            }
            private char[] theSpecialBoundaries;

            internal WordCollection (Document d)
            {
                try
                {
                    useSpecialWordBoundaries = false;
                    document = d;
                    if (d.stopClock.IsRunning)
                    {
                        Trace.TraceInformation (d.stopClock.ElapsedTicks.ToString () + "Word Collection Constructor yo!");

                }
                }
                catch (Exception ex)
                {


                    logger.Error ("An error has occurred. Exception text:{0}", ex.Message);             }

            }
            internal WordCollection (Document d, bool useSpecial)
            {
                try
                {
                    theSpecialBoundaries = new char[] { '.', '[', ']', '_' };
                    document = d;
                    if (d.stopClock.IsRunning)
                    {
                        Trace.TraceInformation ("Word Collection Constructor yo! {0}", d.stopClock.ElapsedMilliseconds.ToString ());

                }
                    useSpecialWordBoundaries = useSpecial;
                }
                catch (Exception ex)
                {
                    logger.Error ("An error has occurred. Exception text:{0}", ex.Message);


                }
            }

            // Helper function -- search character array "text", starting at
            // character "begin", for word number "wordCount." Returns false
            // if there are less than wordCount words. Sets "start" and
            // length" to the position and length of the word within text:

            /// <summary>
            /// Indexer to get and set words of the containing document:
            /// </summary>
            /// <param name="index"></param>
            /// <returns></returns>
            public string this[int index]
            {
                get
                {
                    int start, length;
                    if (GetWord(document.TextArray, 0, index, out start,
                                out length))
                        return new string(document.TextArray, start, length);
                    else
                        throw new IndexOutOfRangeException();
                }
                set
                {
                    int start, length;
                    if (GetWord(document.TextArray, 0, index, out start,
                                out length))
                    {
                        // Replace the word at start/length with the 
                        // string "value":
                        if (length == value.Length)
                        {
                            Array.Copy(value.ToCharArray(), 0,
                                       document.TextArray, start, length);
                        }
                        else
                        {
                            var newText =
                                new char[document.TextArray.Length +
                                         value.Length - length];
                            Array.Copy(document.TextArray, 0, newText,
                                       0, start);
                            Array.Copy(value.ToCharArray(), 0, newText,
                                       start, value.Length);
                            Array.Copy(document.TextArray, start + length,
                                       newText, start + value.Length,
                                       document.TextArray.Length - start
                                       - length);
                            document.TextArray = newText;
                        }
                    }
                    else
                        throw new IndexOutOfRangeException();
                }
            }

            /// <summary>
            /// Get the count of words in the containing document:
            /// </summary>
            public int Count
            {
                get
                {
                    int count = 0, start = 0, length = 0;
                    globalCounter++;
                    while (GetWord(document.TextArray, start + length, 0,
                                   out start, out length))
                    {
                        ++count;
                    }
                  
                    return count;
                }
            }

            private bool GetWord(char[] text, int begin, int wordCount,
                                 out int start, out int length)
            {
               
                int end = text.Length;
                int count = 0;
                int inWord = -1;
                start = length = 0;

                for (int i = begin; i <= end; ++i)
                {
          
                    bool isLetter = true;
                    //if (i > 0 && (i % 10000 == 0))
                    //{

                    //    Trace.TraceInformation("GetWord Loop Iteration : {0} - {1} ms.",i,document.stopClock.ElapsedMilliseconds );

                    //}
                    if (useSpecialWordBoundaries == false)
                    {
                        isLetter = i < end && Char.IsLetterOrDigit(text[i]);
                    }

                    if (useSpecialWordBoundaries == true)
                    {
                        isLetter = i < end && (Char.IsLetterOrDigit(text[i]) || GetSpecialBoundaries.Contains(text[i]));
                    }


                    if (inWord >= 0)
                    {
                        if (!isLetter)
                        {
                            if (count++ == wordCount)
                            {
                                start = inWord;
                                length = i - inWord;
                                return true;
                            }
                            inWord = -1;
                        }
                    }
                    else
                    {
                        if (isLetter)
                            inWord = i;
                    }
                }
              
                return false;
                
            }
        }

        #endregion
    }
}

